﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MPlusTree;
using TSTreeThesis.Utilities;

namespace TSTreeThesis.DataMgr
{
    public class MPTreeKey : MPKey
    {
        private int nDimension;
        private float[] data;
        private int kDimension;//Key dimension
        private int index;

        public MPTreeKey()
        {
            data = null;
            nDimension = 0;
            kDimension = -1;
            index = -1;
        }

        public void SetIndex(int idx)
        {
            index = idx;
        }

        public int GetIndex()
        {
            return index;
        }

        public void SetNumDimension(int numDim)
        {
            nDimension = numDim;
            data = new float[nDimension];
        }

        public int GetNumDimension()
        {
            return nDimension;
        }

        public float[] GetData()
        {
            return data;
        }

        public void SetData(float[] value)
        {
            if (value != null)
            {
                int i;
                nDimension = value.Length;
                data = new float[nDimension];
                for (i = 0; i < nDimension; ++i)
                    data[i] = value[i];
            }
        }

        public void SetData(double[] value)
        {
            if (value != null)
            {
                int i;
                nDimension = value.Length;
                data = new float[nDimension];
                for (i = 0; i < nDimension; ++i)
                    data[i] = (float)value[i];
            }
        }

        public void SetValueAt(int i, float value)
        {
            data[i] = value;
        }

        public override float Distance(MPKey k)
        {
            return Utility.EuclideDistance(data, ((MPTreeKey)k).GetData());
        }

        public override byte[] ToBytes()
        {
            byte[] buff = new byte[Size()];

            int idx = 0;
            byte[] tmp = BitConverter.GetBytes(kDimension);
            Array.Copy(tmp, 0, buff, idx, tmp.Length);
            idx += tmp.Length;

            tmp = BitConverter.GetBytes(nDimension);
            Array.Copy(tmp, 0, buff, idx, tmp.Length);
            idx += tmp.Length;

            tmp = BitConverter.GetBytes(index);
            Array.Copy(tmp, 0, buff, idx, tmp.Length);
            idx += tmp.Length;

            int i;
            for (i = 0; i < nDimension; ++i)
            {
                tmp = BitConverter.GetBytes(data[i]);
                Array.Copy(tmp, 0, buff, idx, tmp.Length);
                idx += tmp.Length;
            }

            return buff;
        }

        public override void LoadFrom(byte[] b, int startIndex)
        {
            int idx = startIndex;
            kDimension = BitConverter.ToInt32(b, idx);
            idx += 4;

            nDimension = BitConverter.ToInt32(b, idx);
            idx += 4;

            index = BitConverter.ToInt32(b, idx);
            idx += 4;

            data = new float[nDimension];
            int i;
            for (i = 0; i < nDimension; ++i)
            {
                data[i] = BitConverter.ToSingle(b, idx);
                idx += 4;
            }
        }

        public override void SetDNO(int d)
        {
            kDimension = d;
        }

        public override float GetDimensionValue(int d)
        {
            if (d >= 0 && d < nDimension)
            {
                return data[d];
            }
            else
            {
                throw new Exception("Dimension out of range");
            }
        }

        public override MPKey Clone()
        {
            MPTreeKey k = new MPTreeKey();
            k.SetDNO(kDimension);
            k.SetIndex(index);
            k.SetNumDimension(nDimension);
            int i;
            for (i = 0; i < nDimension; ++i)
                k.SetValueAt(i, data[i]);

            return k;
        }

        public override int Size()
        {
            return sizeof(int) * 3 + sizeof(float) * nDimension;
        }

        public static int SizeFromDim(int nDim)
        {
            return sizeof(int) * 3 + sizeof(float) * nDim;
        }

        public override int GetDNO()
        {
            return kDimension;
        }

        public override int SetUpDNO(MPKey[] keys)
        {
            float[] arrAvg = new float[nDimension];
            float[] arrVariance = new float[nDimension];
            int i;
            int j;
            for (i = 0; i < nDimension; ++i)
            {
                arrAvg[i] = 0;
                arrVariance[i] = 0;
            }

            int len = keys.Length;
            for (i = 0; i < nDimension; ++i)
            {
                for (j = 0; j < len; ++j)
                {
                    arrAvg[i] += keys[j].GetDimensionValue(i);
                }

                arrAvg[i] /= nDimension;
            }

            int d = -1;
            float max = float.MinValue;
            float x;
            for (i = 0; i < nDimension; ++i)
            {
                for (j = 0; j < len; ++j)
                {
                    x = keys[j].GetDimensionValue(i) - arrAvg[i];
                    arrVariance[i] += x * x;
                }

                if (max < arrVariance[i])
                {
                    d = i;
                    max = arrVariance[i];
                }
            }

            kDimension = d;

            return d;
        }

        public override int SetUpDNO(LinkedList<float[]> keys)
        {
            throw new NotImplementedException();
        }

        public override int SetUpDNO(LinkedList<MPKey> keys)
        {
            float[] arrAvg = new float[nDimension];
            float[] arrVariance = new float[nDimension];
            int i;
            for (i = 0; i < nDimension; ++i)
            {
                arrAvg[i] = 0;
                arrVariance[i] = 0;
            }

            int len = keys.Count;
            for (i = 0; i < nDimension; ++i)
            {

                foreach (MPKey k in keys)
                {
                    arrAvg[i] += k.GetDimensionValue(i);
                }

                arrAvg[i] /= nDimension;
            }

            int d = -1;
            float max = float.MinValue;
            float x;
            for (i = 0; i < nDimension; ++i)
            {
                foreach (MPKey k in keys)
                {
                    x = k.GetDimensionValue(i) - arrAvg[i];
                    arrVariance[i] += x * x;
                }

                if (max < arrVariance[i])
                {
                    d = i;
                    max = arrVariance[i];
                }
            }

            kDimension = d;
            return d;
        }
    }
}
